﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using System.IO;

namespace EmailCenter_SEK53
{
    public class EmailParser
    {
        public NameValueCollection Headers { get; protected set; }

        public DateTime UtcDateTime { get; protected set; }
        public string From { get; protected set; }
        public string To { get; protected set; }
        public string Subject { get; protected set; }
        public string PartnerName { get; protected set; }

        public string content { get; protected set; }

        List<MessagePart> listMsgParts;
        public List<string> listAttachmentFileNames;

        public  EmailParser()
        {
            listMsgParts = null;
            listAttachmentFileNames = null;
        }

        public EmailParser(string emailText)
        {
            listMsgParts = null;
            listAttachmentFileNames = null;
            Parse(emailText);
        }

        public void Parse(string emailText)
        {
            listMsgParts = Util.ParseMessageParts(emailText);

            Headers = Util.ParseHeaders(emailText);

            string ContentType = Headers["Content-Type"];
            From = Headers["From"];
            To = Headers["To"];
            Subject = EmailUtil.ParseSubject(Headers["Subject"]);
            PartnerName = "";

            Regex regexfrom = new Regex(@"\s*(?<Name>[\w\s_=\?\-]*)\s*\<\s*(?<Email>([\w\.\-]+)@([\w\-]+)((\.(\w){2,3})+))\s*\>\s*");
            Match m = regexfrom.Match(From);
            if (m.Success)
            {
                PartnerName = m.Groups["Name"].Value;
                From = m.Groups["Email"].Value;

                Regex regex = new Regex("=\\?(?<charset>[a-zA-Z0-9\\-]+)\\?[a-zA-Z]\\?(?<content>[a-zA-Z0-9/+]*=*)\\?=.*");
                m = regex.Match(PartnerName);
                if (m.Success)
                    PartnerName = EmailUtil.DecodeBase64String(m.Groups["charset"].Value, m.Groups["content"].Value);

            }

            if (Headers["Date"] != null)
                try
                {
                    UtcDateTime =
                      Util.ConvertStrToUtcDateTime(Headers["Date"]);
                }
                catch (FormatException)
                {
                    UtcDateTime = DateTime.MinValue;
                }
            else
                UtcDateTime = DateTime.MinValue;

            //////////////////////////////////////////////////////////////////////////
            //  Find content part
            MessagePart p = EmailUtil.FindContentPart(listMsgParts);
            if (p == null)
                this.content = "";
            else
            {
                string contentType, charset, contentTransferEncoding;

                contentType = p.Headers["Content-Type"];
                charset = "us-ascii"; // default charset
                contentTransferEncoding =
                    p.Headers["Content-Transfer-Encoding"];

                Match m2 = CharsetRegex.Match(contentType);

                if (m2.Success)
                    charset = m2.Groups["charset"].Value;

                if (contentTransferEncoding != null)
                {
                    if (contentTransferEncoding.ToLower() == "base64")
                        content = EmailUtil.DecodeBase64String(charset, p.MessageText);
                    else if (contentTransferEncoding.ToLower() == "quoted-printable")
                        content = EmailUtil.DecodeQuotedPrintableString(p.MessageText);
                    else
                        content = p.MessageText;
                }
            }

            listAttachmentFileNames = EmailUtil.ParseAttachments(listMsgParts);
        }

        public int GetNumberAttachments()
        {
            return listAttachmentFileNames.Count;
        }

        public bool DropAttachmentTo(string path, int index)
        {
            string contentType, contentTransferEncoding;
            foreach (MessagePart p in listMsgParts)
            {
                contentType = p.Headers["Content-Type"];
                contentTransferEncoding = p.Headers["Content-Transfer-Encoding"];
                Match m = NameRegex.Match(contentType);
                if (m.Success)
                {
                    if (m.Groups["filename"].Value.ToLower() == listAttachmentFileNames[index].ToLower())
                    {
                        if (contentTransferEncoding.ToLower() == "base64")
                        {
                            byte[] buffer;
                            buffer = Convert.FromBase64String(p.MessageText);

                            FileStream filestream = new FileStream(path, FileMode.Create);
                            BinaryWriter brwriter = new BinaryWriter(filestream);
                            brwriter.Write(buffer);
                            brwriter.Close();
                            filestream.Close();
                        }
                        //else if (contentTransferEncoding.ToLower() == "quoted-printable")
                        //    content = EmailUtil.DecodeQuotedPrintableString(p.MessageText);
                        else
                        {
                            StreamWriter file = File.CreateText(path);
                            file.Write(p.MessageText);
                            file.Close();
                        }
                        return true;
                    }
                }
            }
            return false;
        }


        protected static Regex CharsetRegex =
           new Regex("charset=\"?(?<charset>[^\\s\"]+)\"?",
           RegexOptions.IgnoreCase | RegexOptions.Compiled);
        protected static Regex NameRegex =
            new Regex("name=\"?(?<filename>[^\\s\"]+)\"?",
            RegexOptions.IgnoreCase | RegexOptions.Compiled);


    }


    class EmailUtil
    {
        protected static Regex QuotedPrintableRegex =
            new Regex("=(?<hexchars>[0-9a-fA-F]{2,2})",
            RegexOptions.IgnoreCase | RegexOptions.Compiled);
        protected static Regex UrlRegex =
            new Regex("(?<url>https?://[^\\s\"]+)",
            RegexOptions.IgnoreCase | RegexOptions.Compiled);
        protected static Regex FilenameRegex =
            new Regex("filename=\"?(?<filename>[^\\s\"]+)\"?",
            RegexOptions.IgnoreCase | RegexOptions.Compiled);
        protected static Regex NameRegex =
            new Regex("name=\"?(?<filename>[^\\s\"]+)\"?",
            RegexOptions.IgnoreCase | RegexOptions.Compiled);

        public static MessagePart FindContentPart(List<MessagePart> listMsgParts)
        {
            foreach (MessagePart p in listMsgParts)
            {
                if (p.ContentType != null)
                    if (p.ContentType.IndexOf("text/html") != -1 ||
                        p.ContentType.IndexOf("text/plain") != -1)
                        return p;

            }
            return null;
        }

        public static string ParseSubject(string subject)
        {
            Regex regex = new Regex("=\\?(?<charset>[a-zA-Z0-9\\-]+)\\?(?<decorder>[a-zA-Z])\\?(?<content>[a-zA-Z0-9/+=_]*=*)\\?=.*");
            Match m;
            string s = "";
            m = regex.Match(subject);
            if (m.Success)
            {
                if (m.Groups["decorder"].Value.ToUpper() == "B")
                    s = DecodeBase64String(m.Groups["charset"].Value, m.Groups["content"].Value);
                else if (m.Groups["decorder"].Value.ToUpper() == "Q")
                    s = DecodeQuotedPrintableString(m.Groups["content"].Value);
            }
            else
                s = subject;

            return s;
        }

        public static List<string> ParseAttachments(List<MessagePart> msgParts)
        {
            List<string> listFileNames = new List<string>();
            foreach (MessagePart p in msgParts)
            {
                string contentType = p.Headers["Content-Type"];
                string contentDisposition = p.Headers["Content-Disposition"];

                Match m;

                if (contentDisposition != null)
                {
                    m = FilenameRegex.Match(contentDisposition);
                    if (m.Success)
                       listFileNames.Add(m.Groups["filename"].Value);
                }
                else if (contentType != null)
                {
                    m = NameRegex.Match(contentType);
                    if (m.Success)
                        listFileNames.Add(m.Groups["filename"].Value);
                }
            }

            return listFileNames;
        }

        protected static Decoder GetDecoder(string charset)
        {
            Decoder decoder;

            switch (charset.ToLower())
            {
                case "utf-7":
                    decoder = Encoding.UTF7.GetDecoder();
                    break;
                case "utf-8":
                    decoder = Encoding.UTF8.GetDecoder();
                    break;
                case "us-ascii":
                    decoder = Encoding.ASCII.GetDecoder();
                    break;
                case "iso-8859-1":
                    decoder = Encoding.ASCII.GetDecoder();
                    break;
                default:
                    decoder = Encoding.ASCII.GetDecoder();
                    break;
            }

            return decoder;
        }

        public static string DecodeBase64String(string charset, string encodedString)
        {
            Decoder decoder = GetDecoder(charset);

            byte[] buffer = Convert.FromBase64String(encodedString);
            char[] chararr = new char[decoder.GetCharCount(buffer,
                0, buffer.Length)];

            decoder.GetChars(buffer, 0, buffer.Length, chararr, 0);

            return new string(chararr);
        }

        public static string DecodeQuotedPrintableString(string encodedString)
        {
            StringBuilder b = new StringBuilder();
            int startIndx = 0;

            MatchCollection matches = QuotedPrintableRegex.Matches(encodedString);

            for (int i = 0; i < matches.Count; i++)
            {
                Match m = matches[i];
                string hexchars = m.Groups["hexchars"].Value;
                int charcode = Convert.ToInt32(hexchars, 16);
                char c = (char)charcode;

                if (m.Index > 0)
                    b.Append(encodedString.Substring(startIndx,
                        (m.Index - startIndx)));

                b.Append(c);

                startIndx = m.Index + 3;
            }

            if (startIndx < encodedString.Length)
                b.Append(encodedString.Substring(startIndx));

            return Regex.Replace(b.ToString(), "=\r\n", "");
        }
    }
}
